<script setup>
import { ref } from 'vue'
import Logo from '@/components/logo.vue'
import {
  GiggleIcon,
  ChatAddIcon,
  UserCircleIcon,
  SystemSumIcon,
  CheckCircleIcon,
} from 'tdesign-icons-vue-next'
const showAside = ref(false)
const loading = ref(false)
const chatList = ref([])
const fetchCancel = ref(null)

// 流式数据加载中
const isStreamLoad = ref(false)
const chatRef = ref(null)
const isShowToBottom = ref(false)
// 是否显示回到底部按钮
const handleChatScroll = function ({ e }) {
  const scrollTop = e.target.scrollTop
  isShowToBottom.value = scrollTop < 0
}
const handleOperation = function (type, options) {
  console.log('handleOperation', type, options)
}
// 消息发送
const query = ref('')
const inputEnter = function () {
  if (loading.value) {
    return
  }
  if (!query.value) return
  const params = {
    avatar: 'https://tdesign.gtimg.com/site/avatar.jpg',
    content: query.value,
    role: 'user',
  }
  chatList.value.unshift(params)
  // 空消息占位
  const params2 = {
    avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png',
    content: '',
    reasoning: '',
    role: 'assistant',
  }
  chatList.value.unshift(params2)
  handleData()
}
const handleData = async () => {
  const lastItem = chatList.value[0]
  loading.value = true
  isStreamLoad.value = true

  await fetchSSE({
    success(result) {
      loading.value = false
      result.data.delta.reasoning_content &&
        (lastItem.reasoning += result.data.delta.reasoning_content)
      result.data.delta.content && (lastItem.content += result.data.delta.content)
    },
    complete(isOk, msg) {
      if (!isOk) {
        lastItem.role = 'error'
        lastItem.content = msg
        lastItem.reasoning = msg
      }
      // 控制终止按钮
      isStreamLoad.value = false
      loading.value = false
    },
  })
}
const fetchSSE = async (options) => {
  let data = {
    messages: chatList.value.slice(1),
    model: isChecked.value ? 'deepseek-reasoner' : 'deepseek-chat',
    frequency_penalty: 0,
    max_tokens: 4096,
    presence_penalty: 0,
    response_format: {
      type: 'text',
    },
    stop: null,
    stream: true,
    stream_options: null,
    temperature: 1,
    top_p: 1,
    tools: null,
    tool_choice: 'none',
    logprobs: false,
    top_logprobs: null,
  }
  query.value = ''
  const { success, fail, complete } = options
  // fetch请求流式接口url，需传入接口url和参数
  const responsePromise = fetch('https://api.deepseek.com/chat/completions', {
    method: 'post',
    responseType: 'user',
    headers: {
      'Content-Type': 'application/json',
      Accept: 'application/json',
      Authorization: 'Bearer sk-ae19700900b04346ba6629e726e05378',
    },
    body: JSON.stringify(data),
  }).catch((e) => {
    const msg = e.toString() || '流式接口异常'
    complete?.(false, msg)
    return Promise.reject(e) // 确保错误能够被后续的.catch()捕获
  })

  responsePromise
    .then((response) => {
      if (!response?.ok) {
        complete?.(false, response.statusText)
        fail?.()
        throw new Error('Request failed') // 抛出错误以便链式调用中的下一个.catch()处理
      }
      const reader = response.body.getReader()
      const decoder = new TextDecoder()
      if (!reader) throw new Error('No reader available')

      const bufferArr = []
      let dataText = '' // 记录数据
      const event = { type: null, data: null }

      async function processText({ done, value }) {
        if (done) {
          complete?.(true)
          return Promise.resolve()
        }
        const chunk = decoder.decode(value)
        const buffers = chunk.toString().split(/\r?\n/)
        bufferArr.push(...buffers)
        const i = 0
        while (i < bufferArr.length) {
          const line = bufferArr[i]
          if (line) {
            dataText += line
            const response = line.slice(6)
            if (response === '[DONE]') {
              event.type = 'finish'
              dataText = ''
            } else {
              const choices = JSON.parse(response.trim())?.choices?.[0]
              if (choices.finish_reason === 'stop') {
                event.type = 'finish'
                dataText = ''
              } else {
                event.type = 'delta'
                event.data = choices
              }
            }
          }
          if (event.type && event.data) {
            const jsonData = JSON.parse(JSON.stringify(event))
            // 回调更新数据
            success(jsonData)
            event.type = null
            event.data = null
          }
          bufferArr.splice(i, 1)
        }
        return reader.read().then(processText)
      }

      return reader.read().then(processText)
    })
    .catch(() => {
      // 处理整个链式调用过程中发生的任何错误
      fail?.()
    })
}
const onStop = function () {
  if (fetchCancel.value) {
    fetchCancel.value.controller.close()
    loading.value = false
    isStreamLoad.value = false
  }
}
// 是否开启深度思考
const isChecked = ref(true)
const checkClick = () => {
  isChecked.value = !isChecked.value
}
</script>

<template>
  <div class="chat_home">
    <div class="chat_aside">
      <div class="fold-aside" v-show="!showAside">
        <logo size="40px" @click="() => (showAside = !showAside)"></logo>
        <GiggleIcon class="arrow" />
        <ChatAddIcon class="arrow" />
        <div style="flex: 1 1 0%"></div>
        <UserCircleIcon class="user" />
      </div>
      <div class="expand-aside" v-show="showAside" @click="() => (showAside = !showAside)">321</div>
    </div>
    <div class="chat_content">
      <div class="title">demo</div>
      <t-chat
        ref="chatRef"
        style="max-width: 800px; margin: 0 auto; height: calc(100% - 46px)"
        :clearHistory="false"
        :data="chatList"
        :text-loading="loading"
        :is-stream-load="isStreamLoad"
        @scroll="handleChatScroll"
      >
        <!-- eslint-disable vue/no-unused-vars -->
        <template #content="{ item, index }">
          <t-chat-reasoning v-if="item.reasoning?.length > 0" expand-icon-placement="right">
            <template #header>
              <t-chat-loading v-if="isStreamLoad && item.content.length === 0" text="思考中..." />
              <div v-else style="display: flex; align-items: center">
                <CheckCircleIcon
                  style="color: var(--td-success-color-5); font-size: 20px; margin-right: 8px"
                />
                <span>已深度思考</span>
              </div>
            </template>
            <t-chat-content v-if="item.reasoning.length > 0" :content="item.reasoning" />
          </t-chat-reasoning>
          <t-chat-content v-if="item.content.length > 0" :content="item.content" />
        </template>
        <template #actions="{ item, index }">
          <t-chat-action
            :content="item.content"
            :operation-btn="['good', 'bad', 'replay', 'copy']"
            @operation="handleOperation"
          />
        </template>
        <template #footer>
          <t-chat-sender
            v-model="query"
            :textarea-props="{
              placeholder: '请输入消息...',
            }"
            :loading="loading"
            @send="inputEnter"
            @stop="onStop"
          >
            <template #prefix>
              <div class="model-select">
                <t-button
                  class="check-box"
                  :class="{ 'is-active': isChecked }"
                  variant="text"
                  @click="checkClick"
                >
                  <SystemSumIcon />
                  <span>深度思考</span>
                </t-button>
              </div>
            </template>
            <template #suffix="{ renderPresets }">
              <component :is="renderPresets([])" />
            </template>
          </t-chat-sender>
          <div class="tips">内容由 AI 生成，请仔细甄别</div>
        </template>
      </t-chat>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.chat_home {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-wrap: nowrap;
  .chat_aside {
    flex-shrink: 0;
    background-color: #f9fbff;
    position: relative;
    min-width: 68px;
    & .fold-aside {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 20px 0;
      width: 68px;
      .arrow {
        font-size: 32px;
        cursor: pointer;
        margin: 20px;
      }
      .user {
        font-size: 32px;
        cursor: pointer;
        color: pink;
      }
    }
    & .expand-aside {
      width: 260px;
    }
  }
  .chat_content {
    display: flex;
    flex-direction: column;
    flex: 1;
    .title {
      flex-shrink: 0;
      text-align: center;
      height: 46px;
      width: 100%;
      font-weight: 550;
      font-size: 18px;
      line-height: 46px;
    }
    .model-select {
      display: flex;
      align-items: center;
      .check-box {
        width: 112px;
        height: var(--td-comp-size-m);
        border-radius: 32px;
        border: 0;
        background: var(--td-bg-color-component);
        color: var(--td-text-color-primary);
        box-sizing: border-box;
        flex: 0 0 auto;
        :deep(.t-button__text) {
          display: flex;
          align-items: center;
          justify-content: center;
          span {
            margin-left: var(--td-comp-margin-xs);
          }
        }
      }
    }
    .check-box.is-active {
      border: 1px solid var(--td-brand-color-focus);
      background: var(--td-brand-color-light);
      color: var(--td-text-color-brand);
    }
    .tips {
      margin: 6px 0;
      line-height: 14px;
      color: rgb(163 163 163);
      font-size: 12px;
      text-align: center;
    }
  }
}
</style>
